#include "Objects.h"
#include <cmath>
#include <GL/gl.h>

static void produtoPorEscalar(float a, float *u, float *t)
{
	t[0] = a*u[0];
	t[1] = a*u[1];
	t[2] = a*u[2];
}

static float produtoInterno(float *u, float *v)
{
	return u[0]*v[0]+u[1]*v[1]+u[2]*v[2];
}

static void dotsToVector(float *u, float *v, float *t)
{
	t[0] = v[0]-u[0];
	t[1] = v[1]-u[1];
	t[2] = v[2]-u[2];
}

static void produtoVetorial(float *u, float *v, float *t)
{
	t[0] = u[1]*v[2]-u[2]*v[1];
	t[1] = u[2]*v[0]-u[0]*v[2];
	t[2] = u[0]*v[1]-u[1]*v[0];
}

void gllibSphere(double _radius, double _divisions) {
  double ratio = M_PI/_divisions;
  double x, y, z;
  double x1, y1, z1;

  glBegin(GL_TRIANGLE_FAN);
  glNormal3f(0.0, _radius, 0.0);
  glVertex3f(0.0, _radius, 0.0);

  y = sin(M_PI/2.0 - ratio) * _radius;
  for(double alpha = M_PI*2.0; alpha >= 0.0; alpha -= ratio) {
    x = cos(alpha) * cos(M_PI/2.0 - ratio) * _radius;
    z = sin(alpha) * cos(M_PI/2.0 - ratio) * _radius;

    glNormal3f(x, y, z);
    glVertex3f(x, y, z);
  }

  glEnd();

  glBegin(GL_TRIANGLE_FAN);
  glNormal3f(0.0, -_radius, 0.0);
  glVertex3f(0.0, -_radius, 0.0);

  y = sin(-M_PI/2.0 + ratio) * _radius;
  for(double alpha = 0.0; alpha <= M_PI*2.0; alpha += ratio) {
    x = cos(alpha) * cos(-M_PI/2.0 + ratio) * _radius;
    z = sin(alpha) * cos(-M_PI/2.0 + ratio) * _radius;

    glNormal3f(x, y, z);
    glVertex3f(x, y, z);
  }
  glEnd();

  for(double beta = M_PI/2.0 - ratio; beta >= -M_PI/2.0 + ratio; beta -= ratio) {
    y = sin(beta) * _radius;
    y1 = sin(beta + -ratio) * _radius;

    glBegin(GL_QUAD_STRIP);
    for(double alpha = M_PI*2.0; alpha >= 0.0; alpha -= ratio) {
      x = cos(alpha) * cos(beta) * _radius;
      z = sin(alpha) * cos(beta) * _radius;

      x1 = cos(alpha) * cos(beta - ratio) * _radius;
      z1 = sin(alpha) * cos(beta - ratio) * _radius;

      glNormal3f(x, y, z);
      glVertex3f(x, y, z);

      glNormal3f(x1, y1, z1);
      glVertex3f(x1, y1, z1);
    }
    glEnd();
  }
}

void gllibCube(double _side) {
  double halfSide = _side/2.0;

  glBegin(GL_QUADS);
  glNormal3f(0.0, 0.0, 1.0);
  glVertex3f(-halfSide, -halfSide, halfSide);
  glVertex3f(halfSide, -halfSide, halfSide);
  glVertex3f(halfSide, halfSide, halfSide);
  glVertex3f(-halfSide, halfSide, halfSide);

  glNormal3f(0.0, 0.0, -1.0);
  glVertex3f(-halfSide, halfSide, -halfSide);
  glVertex3f(halfSide, halfSide, -halfSide);
  glVertex3f(halfSide, -halfSide, -halfSide);
  glVertex3f(-halfSide, -halfSide, -halfSide);

  glNormal3f(1.0, 0.0, 0.0);
  glVertex3f(halfSide, -halfSide, -halfSide);
  glVertex3f(halfSide, halfSide, -halfSide);
  glVertex3f(halfSide, halfSide, halfSide);
  glVertex3f(halfSide, -halfSide, halfSide);

  glNormal3f(-1.0, 0.0, 0.0);
  glVertex3f(-halfSide, -halfSide, halfSide);
  glVertex3f(-halfSide, halfSide, halfSide);
  glVertex3f(-halfSide, halfSide, -halfSide);
  glVertex3f(-halfSide, -halfSide, -halfSide);

  glNormal3f(0.0, 1.0, 0.0);
  glVertex3f(-halfSide, halfSide, -halfSide);
  glVertex3f(-halfSide, halfSide, halfSide);
  glVertex3f(halfSide, halfSide, halfSide);
  glVertex3f(halfSide, halfSide, -halfSide);

  glNormal3f(0.0, -1.0, 0.0);
  glVertex3f(halfSide, -halfSide, -halfSide);
  glVertex3f(halfSide, -halfSide, halfSide);
  glVertex3f(-halfSide, -halfSide, halfSide);
  glVertex3f(-halfSide, -halfSide, -halfSide);
  glEnd();
}

void gllibCylinder(double _radius, double _height, double _divisions) {
  double ratio = M_PI/_divisions;
  double x, y, z;

  glBegin(GL_TRIANGLE_FAN);
  glNormal3f(0.0, 1.0, 0.0);
  glVertex3f(0.0, _height/2.0, 0.0);

  y = _height/2.0;
  for(double alpha = M_PI*2.0; alpha >= 0.0; alpha -= ratio) {
    x = cos(alpha) * _radius;
    z = sin(alpha) * _radius;

    glVertex3f(x, y, z);
  }

  glEnd();

  glBegin(GL_TRIANGLE_FAN);
  glNormal3f(0.0, -1.0, 0.0);
  glVertex3f(0.0, -_height/2.0, 0.0);

  y = -_height/2.0;
  for(double alpha = 0.0; alpha <= M_PI*2.0; alpha += ratio) {
    x = cos(alpha) * _radius;
    z = sin(alpha) * _radius;

    glVertex3f(x, y, z);
  }

  glEnd();

  glBegin(GL_QUAD_STRIP);
  y = _height/2.0;
  for(double alpha = M_PI*2.0; alpha >= 0.0; alpha -= ratio) {
    x = cos(alpha) * _radius;
    z = sin(alpha) * _radius;

    glNormal3f(x, 0.0, z);
    glVertex3f(x, y, z);
    glVertex3f(x, -y, z);
  }
  glEnd();
}

void gllibCone(double _radius, double _height, double _divisions) {
  double ratio = M_PI/_divisions;
  double x, y, z;

  glBegin(GL_TRIANGLE_FAN);
  glNormal3f(0.0, _height/2.0, 0.0);
  glVertex3f(0.0, _height/2.0, 0.0);

  y = -_height/2.0;
  for(double alpha = M_PI*2.0; alpha >= 0.0; alpha -= ratio) {
    x = cos(alpha) * _radius;
    z = sin(alpha) * _radius;

    glNormal3f(x, 0.0, z);
    glVertex3f(x, y, z);
  }

  glEnd();

  glBegin(GL_TRIANGLE_FAN);
  glNormal3f(0.0, -1.0, 0.0);
  glVertex3f(0.0, -_height/2.0, 0.0);

  y = -_height/2.0;
  for(double alpha = 0.0; alpha <= M_PI*2.0; alpha += ratio) {
    x = cos(alpha) * _radius;
    z = sin(alpha) * _radius;

    glVertex3f(x, y, z);
  }

  glEnd();
}

void gllibTorus(double _inRadius, double _outRadius, double _inDivision, double _outDivision) {
  double inRatio = M_PI/_inDivision;
  double outRatio = M_PI/_outDivision;
  double x, y, z;
  double x1, y1, z1;

  for(double beta = 0.0; beta <= 2.0 * M_PI + inRatio; beta += inRatio) {
    glBegin(GL_QUAD_STRIP);
    for(double alpha = 0.0; alpha <= 2.0 * M_PI + outRatio; alpha += outRatio) {
      double cx = _outRadius * cos(alpha);
      double cz = _outRadius * sin(alpha);

      x = (_outRadius + _inRadius * cos(beta)) * cos(alpha);
      y = _inRadius * sin(beta);
      z = (_outRadius + _inRadius * cos(beta)) * sin(alpha);

      x1 = (_outRadius + _inRadius * cos(beta + inRatio)) * cos(alpha);
      y1 = _inRadius * sin(beta + inRatio);
      z1 = (_outRadius + _inRadius * cos(beta + inRatio)) * sin(alpha);

      glNormal3f(x - cx, y, z - cz);
      glVertex3f(x, y, z);

      glNormal3f(x1 - cx, y1, z1 - cz);
      glVertex3f(x1, y1, z1);
    }
    glEnd();
  }
}

void gllibCircumference(double _radius, double _division) {
  double ratio = M_PI/_division;
  double x, y;

  glBegin(GL_LINE_LOOP);

  for(double alpha = 0.0; alpha <= 2*M_PI; alpha += ratio) {
    x = cos(alpha) * _radius;
    y = sin(alpha) * _radius;

    glNormal3f(x, y, 0);
    glVertex3f(x, y, 0);
  }

  glEnd();
}

void gllibEightDots(
								float x0, float y0, float z0,
								float x1, float y1, float z1,
								float x2, float y2, float z2,
								float x3, float y3, float z3,
								float x4, float y4, float z4,
								float x5, float y5, float z5,
								float x6, float y6, float z6,
								float x7, float y7, float z7)
{
	float vertices[8][3];

	vertices[0][0] = x0; vertices[0][1] = y0; vertices[0][2] = z0;
	vertices[1][0] = x1; vertices[1][1] = y1; vertices[1][2] = z1;
	vertices[2][0] = x2; vertices[2][1] = y2; vertices[2][2] = z2;
	vertices[3][0] = x3; vertices[3][1] = y3; vertices[3][2] = z3;
	vertices[4][0] = x4; vertices[4][1] = y4; vertices[4][2] = z4;
	vertices[5][0] = x5; vertices[5][1] = y5; vertices[5][2] = z5;
	vertices[6][0] = x6; vertices[6][1] = y6; vertices[6][2] = z6;
	vertices[7][0] = x7; vertices[7][1] = y7; vertices[7][2] = z7;

	float u[3], v[3], normal[3], lenght;

	glBegin(GL_QUADS);

		dotsToVector(vertices[0],vertices[3],u);
		dotsToVector(vertices[0],vertices[2],v);
		produtoVetorial(u,v,normal);
		lenght = sqrt(produtoInterno(normal,normal));
		produtoPorEscalar(1.0/lenght,normal,normal);
		glNormal3fv(normal);
		glVertex3fv(vertices[0]);
		glVertex3fv(vertices[3]);
		glVertex3fv(vertices[2]);
		glVertex3fv(vertices[1]);

		dotsToVector(vertices[1],vertices[2],u);
		dotsToVector(vertices[1],vertices[6],v);
		produtoVetorial(u,v,normal);
		lenght = sqrt(produtoInterno(normal,normal));
		produtoPorEscalar(1.0/lenght,normal,normal);
		glNormal3fv(normal);
		glVertex3fv(vertices[1]);
		glVertex3fv(vertices[2]);
		glVertex3fv(vertices[6]);
		glVertex3fv(vertices[5]);

		dotsToVector(vertices[4],vertices[5],u);
		dotsToVector(vertices[4],vertices[6],v);
		produtoVetorial(u,v,normal);
		lenght = sqrt(produtoInterno(normal,normal));
		produtoPorEscalar(1.0/lenght,normal,normal);
		glNormal3fv(normal);
		glVertex3fv(vertices[4]);
		glVertex3fv(vertices[5]);
		glVertex3fv(vertices[6]);
		glVertex3fv(vertices[7]);

		dotsToVector(vertices[3],vertices[0],u);
		dotsToVector(vertices[3],vertices[4],v);
		produtoVetorial(u,v,normal);
		lenght = sqrt(produtoInterno(normal,normal));
		produtoPorEscalar(1.0/lenght,normal,normal);
		glNormal3fv(normal);
		glVertex3fv(vertices[3]);
		glVertex3fv(vertices[0]);
		glVertex3fv(vertices[4]);
		glVertex3fv(vertices[7]);

		dotsToVector(vertices[0],vertices[1],u);
		dotsToVector(vertices[0],vertices[5],v);
		produtoVetorial(u,v,normal);
		lenght = sqrt(produtoInterno(normal,normal));
		produtoPorEscalar(1.0/lenght,normal,normal);
		glNormal3fv(normal);
		glVertex3fv(vertices[0]);
		glVertex3fv(vertices[1]);
		glVertex3fv(vertices[5]);
		glVertex3fv(vertices[4]);

		dotsToVector(vertices[2],vertices[3],u);
		dotsToVector(vertices[2],vertices[7],v);
		produtoVetorial(u,v,normal);
		lenght = sqrt(produtoInterno(normal,normal));
		produtoPorEscalar(1.0/lenght,normal,normal);
		glNormal3fv(normal);
		glVertex3fv(vertices[2]);
		glVertex3fv(vertices[3]);
		glVertex3fv(vertices[7]);
		glVertex3fv(vertices[6]);

	glEnd();
}

void gllibEixoY(void)
{
    glColor3f(0.0, 0.0, 0.0);
    glPushMatrix();
        glScalef(1.0,300.0,1.0);
        gllibCube(0.02);
    glPopMatrix();
}

// Tronco:
void gllibFrustum(double _radius_up, double _radius_bottom, double _height, double _divisions)
{
  double ratio = M_PI/_divisions;
  double x, y, z;

  // Base menor:
  glBegin(GL_TRIANGLE_FAN);
  glNormal3f(0.0, 1.0, 0.0);
  glVertex3f(0.0, _height/2.0, 0.0);

  y = _height/2.0;
  for(double alpha = M_PI*2.0; alpha >= 0.0; alpha -= ratio) {
    x = cos(alpha) * _radius_up;
    z = sin(alpha) * _radius_up;

    glVertex3f(x, y, z);
  }
  glEnd();

  // Base maior:
  glBegin(GL_TRIANGLE_FAN);
  glNormal3f(0.0, -1.0, 0.0);
  glVertex3f(0.0, -_height/2.0, 0.0);

  y = -_height/2.0;
  for(double alpha = 0.0; alpha <= M_PI*2.0; alpha += ratio) {
    x = cos(alpha) * _radius_bottom;
    z = sin(alpha) * _radius_bottom;

    glVertex3f(x, y, z);
  }
  glEnd();

  // Lateral:
  double topo_x,topo_z;
  glBegin(GL_QUAD_STRIP);
  y = _height/2.0;
  for(double alpha = M_PI*2.0; alpha >= 0.0; alpha -= ratio) {
    x = cos(alpha) * _radius_bottom;
    z = sin(alpha) * _radius_bottom;
    topo_x = cos(alpha) * _radius_up;
    topo_z = sin(alpha) * _radius_up;

    glNormal3f(x, 0.0, z);
    glVertex3f(topo_x, y, topo_z);
    glVertex3f(x, -y, z);
  }
  glEnd();
}
